Протоколювання обміну інформацією між комп`ютером і зовнішнім запам`ятовуючим USB-пристроєм

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Факультет «Інформатика та системи управління»

Кафедра "Програмне забезпечення ЕОМ та інформаційні технології»

Курсовий проект

з системного програмування

Розрахунково-пояснювальна записка

Тема:

«Протоколювання обміну інформацією між комп'ютером і зовнішнім запам'ятовуючим USB пристроєм »

Зміст

Введення

1. Аналітичний розділ

1.1 Постановка завдання

1.2 Архітектура Windows NT 5

1.3 Шина USB

1.3.1 Внутрішня організація шини USB

1.4 драйверне модель WDM

1.4.1 драйверне шари

1.4.2 Точки входу WDM-драйвера

1.5 Пакет запиту вводу / виводу (IRP)

1.6 Рівні запиту переривань

1.7 Повідомлення про завершення запиту нижчестоящим драйвером

1.8 Робота з файлами в режимі ядра

1.9 Робота з реєстром в режимі ядра

1.10 MDL списки

2. Конструкторський розділ

2.1 Точки входу розроблюваного драйвера

2.1.1 Функція DriverEntry

2.1.2 Функція AddDevice

2.1.3 Функція DriverUnload

2.1.4 Функція DispatchRoutine

2.1.5 Функція DispatchInternalDeviceControl

2.2 Розміщення коду драйвера в пам'яті

2.3 Установка драйвера в системі

3. Технологічний розділ

3.1 Вибір мови і засобів програмування

3.1.1 Драйвер-фільтр

3.1.2 Управляє додаток

3.2 Структури даних драйвера-фільтра

3.2 Інтерфейс керуючого програми

3.3 Тестування драйвера-фільтра

Висновок

Список літератури та інтернет-ресурсів

Введення

При вирішенні широкого кола завдань виникає необхідність в отриманні інформації про функціонування будь-якого зовнішнього пристрою комп'ютера. До категорії цих завдань можна віднести розробку систем інформаційної безпеки, що дуже актуально в сучасному світі, де інформація є одним з найважливіших ресурсів.

Одним з компонентів системи інформаційної безпеки може бути модуль, що виконує протоколювання обміну інформацією між комп'ютером і деяким зовнішнім запам'ятовуючим пристроєм, наприклад USB накопичувачем.

1. Аналітичний розділ

1.1 Постановка завдання

Відповідно до завдання на курсову роботу необхідно розробити програмний комплекс, що забезпечує спостереження за обміном інформацією між комп'ютером і зовнішнім запам'ятовуючим USB пристроєм.

Перерахуємо вимоги, які пред'являються до програмного комплексу:

  • Розроблюваний комплекс повинен відстежувати запити на читання і запис, що приходять до пристрою;

  • Від програми не потрібно визначати, до яких файлів вироблялося звернення, а лише те, які дані зчитувалися і записувалися;

  • Збережена інформація повинна володіти структурованістю, таким чином, що при аналізі лог-файл, можна було визначити, передавалася або приймалася інформація і в якій кількості;

  • Програмний комплекс не повинен призводити до збоїв у роботі операційної системи;

  • Програма установки повинна коректно обробляти спробу встановлення на один пристрій у двох примірниках.

1.2 Архітектура Windows NT 5

Архітектура Windows NT 5 відповідає класичним уявленням про проектування операційних систем. Найбільш поширені реалізації даної ОС для платформи Intel x 86 у одно-або багатопроцесорних конфігураціях, проте існують також версії для DEC Alpha і MIPS. Дана операційна система використовує захищений режим центрального процесора, реалізує механізми віртуальної пам'яті і багатозадачності.

Виконуваний код у Windows NT 5 має два рівні привілеїв: код режиму користувача і код режиму ядра. Рівень привілеїв накладає певні обмеження: у режимі користувача не можуть виконуватися привілейовані інструкції процесора, що не дозволено звернення до захищених сторінок пам'яті. Ці обмеження накладаються для забезпечення безпеки роботи системи. Для користувача додаток не повинно мати можливість в результаті помилки або навмисно вносити зміни в критичні таблиці операційної системи або в пам'ять інших додатків. Зокрема, такі обмеження забороняють користувача додатком безпосередньо керувати зовнішніми пристроями, тому що кожне з них є ресурсом, що розділяється.

У Windows NT 5 забезпечення обміну даними і управління доступом до зовнішнього пристрою як до ресурсу покладається на його драйвер. Введення і виведення в драйверах здійснюється за допомогою IRP пакетів (Input / Output Request Packet). Запити на введення / висновок, що їх посилають додатками або іншими драйверами, обробляються драйвером, після чого запитуючої програмою в тому ж пакеті надсилається статус завершення операції. Детальніше про пакети вводу / виводу буде сказано далі, загальний же принцип взаємодії проілюстровано на Рис. 1.2.1.

Рис. 1.2.2 Архітектура введення / виводу Windows NT 5

Стосовно до поставленого завдання, з вищевикладеного випливає, що повне протоколювання обміну даними із зовнішнім пристроєм може бути здійснене тільки на рівні драйвера.

Управління зовнішнім пристроєм в загальному випадку зводиться до заповнення його регістрів необхідними даними. Монопольний доступ драйвера до цих регістрів гарантується операційною системою. Очевидно, що за даних обставин потрібно, щоб драйвер пристрою виконувався в режимі ядра.

Узагальнена класифікація драйверів Windows NT 5 може бути представлена ​​наступним чином:

  • Драйвери режиму ядра:

  • Успадковані драйвери;

  • Драйвери файлової системи;

  • Відеодрайвери;

  • Драйвери PnP (Plug And Play):

    • Драйвери WDM.

  • Драйвери користувальницького режиму.

1.3 Шина USB

Специфікація USB була розроблена консорціумом компаній, включаючи Intel та Microsoft. Метою нового стандарту було забезпечення організації недорогий средньошвидкісною шини в таких областях застосування, як передача цифрових зображень, комп'ютерна телефонія та мультимедійні ігри. Поточними версіями специфікації USB є версії 1.1 та 2.0 (в другу закладені більш високі швидкісні характеристики).

Гранична швидкість передачі даних по шині USB специфікації 1.1 складає 12 Мбіт / с (Full Speed). Повільні пристрої використовують низьку швидкість передачі - 1,5 Мбіт / с (Low Speed). Стандарт USB версії 2.0 підтримує фізичну швидкість передачі до 480 Мбіт / с (High Speed). Дані передаються послідовно по парі провідників. Харчування для деяких пристроїв доступно за окремим провідникам харчування та заземлення (для пристроїв з невеликим енергоспоживанням).

Пристрої USB можуть бути підключені 5 - метровим кабелем (а практично - і більш довгим). Використання USB хаба (hub - концентратор) дозволяє збільшити дальність розміщення пристроїв від хост-контролера, а так само кількість пристроїв, що підключаються до однієї шині USB. Послідовно можна підключити до п'яти хабів, забезпечивши довжину з'єднання 30 метрів. До хост-контролера можна підключити до 127 пристроїв, шинний адресу яких встановлюється динамічно при підключенні пристроїв.

На малюнку 1.3.1 наведений приклад конфігурації мережі USB   пристроїв:

Рис. 1.3.1 Мережа USB пристроїв

Робота програміста, що створює драйвер зовнішнього (не знаходиться на материнській платі) USB пристрою зводиться до того, щоб скористатися програмним інтерфейсом системних драйверів шини USB, спілкування з яким відбувається за допомогою пакетів, званих URB (USB Request Block) пакетами. Робота з регістрами USB контролерів на материнській платі тепер стала долею вузького кола фахівців - розробників материнських плат та операційних систем. Всім іншим розробникам USB пристроїв в операційній системі Windows пропонується достатньо розвинений програмний інтерфейс WDM драйверів, які беруть на себе всі апаратно-орієнтовані операції.

1.3.1 Внутрішня організація шини USB

Всі операції з передачі даних по шині USB ініціюються хостом. Периферійні пристрої не можуть самі почати обмін даними, вони можуть тільки реагувати на команди хоста. Розглянемо загальну схему обміну даними по шині USB.

Система USB поділяється на три логічних рівня з певними правилами взаємодії. Пристрій USB містить інтерфейсну, логічну і функціональну частини. Хост теж ділиться на три частини: інтерфейсну, системну та програмне забезпечення. Кожна частина відповідає тільки за певне коло завдань. Логічне і реальна взаємодія між ними показано на малюнку 1.3.1.1.

Рис. 1.3.1.1 Взаємодія компонентів USB

Таким чином, операція обміну даними між прикладною програмою і шиною USB виконується шляхом передачі буферів пам'яті через такі рівні:

  • рівень клієнтського програмного забезпечення в хості - зазвичай представляється драйвером пристрою USB, забезпечує взаємодію користувача з операційною системою з одного боку і системним драйвером з іншого;

  • рівень системного програмного забезпечення USB в хості (USBD, Universal Serial Bus Driver) - керує нумерацією пристроїв на шині, управляє розподілом пропускної здатності шини та потужності харчування, обробляє запити користувача драйверів;

  • хост-контролер інтерфейсу шини USB (HCD, Host Controller Driver) - перетворює запити вводу / виводу в структури даних, за якими хост-контролер виконує фізичні транзакції, працює з регістрами хост-контролера.

Рівень клієнтського програмного забезпечення визначає тип передачі даних, необхідний для виконання затребуваної прикладної програмою операції. Після визначення типу передачі даних цей рівень передає системного рівня наступне:

  • буфер пам'яті, званий клієнтським буфером;

  • пакет IRP, який вказує тип необхідної операції. Безпосередньою обробкою запиту займається системний драйвер USB.

Рівень системного драйвера USB необхідний для управління ресурсами USB. Він відповідає за виконання наступних дій:

  • розподіл смуги пропускання шини USB;

  • призначення логічних адрес пристроїв кожній фізичній USB пристрою;

  • планування транзакцій.

Логічне пристрій USB являє собою набір незалежних кінцевих точок, з якими клієнтське програмне забезпечення обмінюється інформацією. Кожному логічного пристрою USB призначається свою адресу, унікальний на даній шині USB. Кожна кінцева точка логічного пристрою ідентифікується своїм номером і напрямом передачі (IN - передача до хоста, OUT - від хоста).

Транзакція на шині USB - це послідовність обміну пакетами між хостом і периферійним пристроєм, в ході якої може бути переданий або прийнятий один пакет даних. Коли клієнтське програмне забезпечення передає IRP рівню системного драйвера, USB драйвер перетворює їх в одну або кілька транзакцій шини і потім передає вийшов перелік транзакцій драйверу контролера хоста.

Системний драйвер USB складається з драйвера USB і драйвера хост-контролера. Коли клієнтський рівень передає IRP рівню системного забезпечення USB, USB драйвер перетворює їх в одну або кілька транзакцій шини і потім передає вийшов перелік транзакцій драйверу контролера хоста. Драйвер контролера хоста приймає від системного драйвера шини перелік транзакцій і виконує наступні дії:

  • планує виконання отриманих транзакцій, додаючи їх до списку транзакцій;

  • витягує зі списку чергову транзакцію, і передає її рівню хост-контролера інтерфейсу шини USB;

  • відстежує стан кожної транзакції аж до її завершення.

При виконанні всіх пов'язаних з командним пакетом транзакцій системний рівень повідомляє про це клієнтський рівень.

Рівень хост-контролера інтерфейсу шини USB отримує окремі транзакції від драйвера контролера хоста (у складі рівня системного забезпечення USB) і перетворює їх у відповідну послідовність операцій шини. У результаті цього USB пакети передаються вздовж усієї фізичної ієрархії хабів до периферійного USB пристрою.

Нижній рівень периферійного USB пристрою називається рівнем інтерфейсу шини USB. Він взаємодіє з інтерфейсним рівнем шини USB на стороні хоста і передає пакети даних від хоста до периферійного пристрою у форматі, визначеному специфікацією USB. Потім він передає пакети вгору - рівню логічного USB пристрою.

Середній рівень периферійного пристрою USB пристрою називається рівнем логічного USB пристрою. Кожне логічне USB пристрій представляється набором своїх кінцевих точок, з якими може взаємодіяти системний рівень USB хоста. Ці точки є джерелами і приймачами всіх комунікаційних потоків між хостом і периферійними пристроями USB.

Самий верхній рівень периферійного USB пристрою називається функціональним рівнем. Цей рівень відповідає рівню клієнтського забезпечення хоста. З точки зору клієнтського рівня, нижчележащі рівні потрібні для організації між ним і кінцевими точками прямих «каналів даних», які йдуть аж до функціонального рівня. А з точки зору нашої схеми функціональний рівень виконує наступні дії:

  • отримує дані, що їх посилають клієнтським рівнем хоста з кінцевих точок каналів даних нижчого рівня логічного USB пристрої;

  • посилає дані клієнтського рівня хоста, направляючи їх в кінцеві точки каналів даних нижчого рівня логічного USB пристрою.

Логічно передача даних між кінцевою точкою і програмним забезпеченням проводиться за допомогою виділення каналу і обміну даними по цьому каналу, а з точки зору представлених рівнів, передача даних виглядає наступним чином:

Рис. 1.3.1.2 Рівні передачі даних

Кінцева точка (Endpoint) - це частина USB пристрою, яка має унікальний ідентифікатор і є одержувачем або відправником інформації, що передається по шині USB. Простіше кажучи, це буфер, який зберігає кілька байт. Зазвичай це блок даних у пам'яті чи регістр мікроконтролера. Дані, що зберігаються в кінцевій точці, можуть бути або прийнятими даними, або даними, які очікували передачу. Хост також має буфер для прийому і передачі даних, але хост не має кінцевих точок.

Кінцева точка має наступні основні параметри:

  • частота доступу до шини;

  • допустима величина затримки обслуговування;

  • необхідна ширина смуги пропускання каналу;

  • номер кінцевої точки;

  • спосіб обробки помилок;

  • максимальний розмір пакету, який кінцева точка може приймати або відправляти;

  • використовуваний кінцевою точкою тип посилок;

  • напрям передачі даних.

Будь-яке USB пристрій має кінцеву точку з нульовим номером (Endpoint Zero). Ця точка дозволяє хосту опитувати пристрій з метою визначення його типу і параметрів, виконувати ініціалізацію та конфігурування пристрою.

Крім нульової точки, пристрої, звичайно, мають додаткові кінцеві точки, які використовуються для обміну даними з хостом. Додаткові точки можуть працювати або тільки на прийом даних від хоста (вхідні точки, IN), або тільки на передачу даних хосту (вихідні точки, OUT).

Нульова точка пристрою доступна після того, як пристрій підключено до шини, включено і отримало сигнал скидання по шині (bus reset). Всі інші кінцеві точки після включення живлення або скидання перебувають у невизначеному стані і недоступні для роботи до тих пір, поки хост не виконає процедуру конфігурування пристрою.

Специфікація шини визначає чотири різних типу передачі даних для кінцевих точок:

  • керуючі передачі (Control Transfers) - використовуються хостом для конфігурування пристрою під час підключення, для керування пристроєм і отримання статусної інформації в процесі роботи. Протокол забезпечує гарантовану доставку таких посилок;

  • передачі масивів даних (Bulk Data Transfers) - застосовуються при необхідності забезпечення гарантованої доставки даних від хоста до функції або від функції до хоста, але час доставки не обмежена;

  • передачі по перериваннях (Interrupt Transfers) - використовують у тому випадку, коли потрібно передавати одиночні пакети даних невеликого розміру. Кожен пакунок потрібен передати за обмежений час. Операції передачі носять спонтанний характер і повинні обслуговуватися не повільніше, ніж того вимагає пристрій;

  • ізохронних передачі (Isochronous Transfers) - застосовуються для обміну даними в «реальному часі», коли на кожному часовому інтервалі потрібно передавати строго певну кількість даних, але доставка інформації не гарантована (передача даних ведеться без повторення при збоях, допускається втрата пакетів).

Канал (Pipe) - це логічне з'єднання між кінцевою точкою пристрої та ПЗ хоста. Існує дві моделі каналів:

  • потоковий канал (або просто потік, streaming pipe) - це канал для передачі даних, структура яких визначається клієнтським ПЗ. Потоки використовуються для передачі масивів даних, передачі даних по перериваннях і ізохронної передачі даних. Потік завжди односпрямований. Один і той же номер кінцевої точки може використовуватися для двох різних потокових каналів - введення і виведення. Передачі даних в потокових каналах підпорядковуються наступним правилам:

  • запити клієнтських драйверів для різних каналів, поставлені в певному порядку один відносно одного, можуть виконуватися в іншому порядку;

  • запити для одного каналу будуть виконуватися строго в порядку їх надходження;

  • якщо під час виконання будь-якого запиту відбувається серйозна помилка (STALL), потік зупиняється;

  • канал повідомлень (message pipe або control pipe) - це канал для передачі даних, структура яких визначається специфікацією USB. Канали цього типу двонаправлені і застосовуються для передачі керуючих посилок. Канали повідомлень суворо синхронізовані - специфікація USB забороняє одночасну обробку декількох запитів: не можна починати передачу нового повідомлення, поки не завершена обробка попереднього. У разі виникнення помилки передача повідомлення може бути перервана хостом, після чого хост може почати передачу нового повідомлення.

Основними характеристиками каналів є:

  • смуга пропускання каналу;

  • використовуваний каналом тип передачі даних;

  • характеристики, що відповідають кінцевій точці: напрямок передачі даних і максимальний розмір пакету.

Смуга пропускання шини ділиться між усіма встановленими каналами. Виділена смуга закріплюється за каналом, і якщо встановлення нового каналу вимагає такої смуги, яка не списується у вже існуючий розподіл, запит на виділення каналу відкидається. Архітектура USB передбачає внутрішню буферизацію всіх пристроїв, причому, чим більшою смуги пропускання вимагає пристрій, тим більше повинен бути його буфер. Шина USB повинна забезпечувати обмін з такою швидкістю, щоб затримка даних у пристрої, викликана буферизацією, не перевищувала декількох мілісекунд.

Канал повідомлень, пов'язаний з нульовою кінцевою точкою, називається Основним каналом повідомлень (Default Control Pipe або Control Pipe 0). Власником цього каналу є USBD, і він використовується для конфігурування пристрою. Основний канал повідомлень підтримує тільки керуючі передачі. Решта канали (вони називаються клієнтськими каналами, Client Pipe) створюються в процесі конфігурування пристрою. Їх власниками є драйвери пристроїв. По клієнтських каналах можуть передаватися як потоки, так і повідомлення за допомогою будь-яких типів передач.

Набір клієнтських каналів, з якими працює драйвер пристрою, називається інтерфейсом пристрою або зв'язкою клієнтських каналів.

1.4 драйверне модель WDM

WDM (Windows Driver Model) - нова модель архітектури драйверів, запропонована Microsoft для Windows 2000, хоча ця архітектура розвивалася, починаючи з Windows 3.11, продовжуючи розвиватися і в Windows 98 і Windows NT, але по-справжньому повної вона стала тільки в Windows 2000.

З точки зору WDM, існує три типи драйверів:

  • Драйвер шини - драйвер, обслуговуючий контролер шини, адаптер, міст або будь-які інші пристрої, що мають дочірні пристрою. Для кожного типу шини в операційній системі є свій драйвер;

  • Функціональний драйвер - основний драйвер пристрою, що надає його функціональний інтерфейс. Цей драйвер обов'язковий крім тих випадків, коли введення-виведення здійснюється драйвером шини або драйвером фільтрів шини. Функціональний драйвер за визначенням має найбільш повною інформацією про своє пристрої. Зазвичай тільки цей драйвер має доступ до специфічних регістрів пристрою;

  • Драйвер фільтра - драйвер, що підтримує додаткову функціональність пристрою (або існуючого драйвера) або змінює запити вводу / виводу і відповіді на них від інших драйверів. Таких драйверів може бути декілька, хоча їх присутність необов'язкова. Вони можуть працювати як на більш високому рівні, ніж функціональний драйвер або драйвер шини, так і на більш низькому.

У середовищі WDM один драйвер не може контролювати всі аспекти пристрої: драйвер шини інформує диспетчера PnP ​​про пристрої, підключених до шини, в той час як функціональний драйвер управляє пристроєм.

1.4.1 драйверне шари

Згідно перерахованим вище типам драйверів, існує три типи
об'єктів:

  • Об'єкти фізичних пристроїв (PDO, Physical Device Object) - ці об'єкти створюються для кожного фізично ідентифікованого елемента апаратури, підключеного до шини даних;

  • Об'єкти функціональних пристроїв (FDO, Functional Device Object) - передбачає одиницю логічної функціональності пристрою;

  • Об'єкти фільтрів пристроїв (FiDO, Filter Device Object) - надають додаткову функціональність.

У Windows NT 5 послідовність завантаження драйверів пристроїв така:

    1. Під час завантаження операційної системи проводиться завантаження шинних драйверів для кожної відомій системі шини (список шин створюється при установці операційної системи і зберігається в реєстрі);

    2. Викликається DriverEntry, а потім AddDevice для кожного шинного драйвера. У AddDevice створюється FDO для драйвера системної шини. Потім на створений FDO відправляється запит IRP _ MN _ START _ DEVICE;

    3. Шинний драйвер складає список всіх пристроїв, підключених до шини. Для кожного знайденого пристрою створюється об'єкт PDO;

    4. На кожен PDO надсилається запит IRP _ MN _ QUERY _ DEVICE _ RELATION, у відповідь на який шинний драйвер повертає ідентифікатори всіх знайдених пристроїв;

    5. На ці PDO надсилають запит IRP _ MN _ QUERY _ ID, у відповідь на який драйвер системної шини повідомляє ідентифікатори цих пристроїв;

    6. Отримавши ідентифікатори, система намагається знайти і завантажити драйвери пристроїв;

    7. Знайшовши драйвер для пристроїв, система завантажує його в пам'ять, викликаючи його DriverEntry. Потім викликається AddDevice, де створюється FDO для пристрою. Якщо пристроїв, керованих цим драйвером, декілька, то AddDevice буде викликана для кожного пристрою. Якщо в реєстрі зареєстровано додаткові фільтри, то вони також завантажуються в пам'ять. Потім система посилає на FDO запит IRP _ MN _ START _ DEVICE;

    1. Відбувається посилка на FDO запиту IRP _ MN _ QUERY _ DEVICE _ RELATIONS. Якщо пристрій сам є шиною чи тримає на собі інші пристрої, якими само не керує, то для пристрою на ньому повторюється вся послідовність дій, починаючи з пункту 5.

Функція AddDevice, що викликається для кожного FDO, викликає IoCreateDevice і IoAttachDeviceToStack, забезпечуючи побудова стека пристроїв. Стек пристроїв забезпечує проходження запитів від користувача програм до апаратного (нижнього) рівня драйверів (Мал. 1.4.1.1).

Рис. 1.4.1.1 Стек пристроїв

З вищесказаного стає зрозумілим, що розробляється драйвер повинен бути драйвером-фільтром нижнього рівня, пов'язаних з клієнтським драйвером USB накопичувача. Необхідність роботи з клієнтським USB драйвером пояснюється тим, що саме на цьому рівні перехоплюється інформація має необхідної структурованістю - передаються саме файли, а не блоки інформації (кадри або складені з них транзакції), що визначаються протоколом обміну через USB.

1.4.2 Точки входу WDM - драйвера

WDM драйвери відрізняються від успадкованих драйверів тим, що повинні містити додаткові точки входу для підтримки PnP, і, в цілому, вони більш логічні по структурі. Наведемо список точок входу і коротко охарактеризуємо їх призначення:

NTSTATUS DriverEntry (

IN PDRIVER _ OBJECT DriverObject, / / покажчик на об'єкт драйвера

IN PUNICODE _ STRING RegistryPath) / / шлях до підрозділу регістра,

/ / Відноситься до драйвера

Ця функція виконується при завантаженні драйвера операційною системою. У WDM драйвери на DriverEntry покладені обов'язки по реєстрації всіх інших точок входу драйвера.

NTSTATUS AddDevice (

IN PDRIVER_OBJECT DriverObject, / / покажчик на об'єкт драйвера

IN PDEVICE _ OBJECT PhysicalDeviceObject) / / покажчик на батьківський PDO

У драйверах, що підтримують PnP, через цю точку входу менеджер PnP посилає драйверу повідомлення про виявлення пристрою, за яке має відповідати драйвер. Функція AddDevice повинна створити об'єкт пристрою за допомогою виклику IoCreateDevice і при необхідності приєднати його до стека пристроїв з допомогою IoAttachDeviceToDeviceStack.

NTSTATUS DriverUnload (

IN PDRIVER_OBJECT DriverObject) / / покажчик на об'єкт драйвера

Викликається при вивантаженні драйвера. У цієї функції повинні осво
Бождан всі витребувані драйвером ресурси. Драйвери WDM моделі
виконують ці дії в обробнику запитів IRP _ MJ _ PNP з субкод IRP _ MN _ REMOVE _ DEVICE, тобто при видаленні пристрою з системи.

Слід виділити окремий клас точок входу драйвера, які призначені для обробки IRP пакетів з різними кодами операцій. Ці точки входу реєструються при завантаженні драйвера у функції DriverEntry. Реєстрація проводиться шляхом заповнення елементів масиву MajorFunction адресами діспетчерізуемих функцій. Індексом в цьому масиві є коди IRP _ MJ _ XXX, то є описані числами типи пакетів IRP. Диспетчер введення / виведення, орієнтуючись на заповнення цього масиву, викликає потрібні функції драйвера.

Оскільки для драйвера важливі тільки адреси робочих процедур, то всі робочі процедури можуть мати абсолютно довільні імена.

1.5 Пакет запиту вводу / виводу (IRP)

Пакети вводу / виводу (IRP пакети) використовуються для передачі запитів до драйвера від його клієнтів. Вони є структурами даних змінної довжини, і складаються із стандартного заголовка, що містить загальну облікову інформацію, і одного або декількох блоків параметрів, які називаються осередками стека вводу / виводу (I / O Stack Location).

Наведемо структуру заголовка IRP пакету:

Таблиця 1.5.1. Структура заголовка IRP пакета.

Поля

Опис

IO_STATUS_BLOCK IoStatus

Статус запиту

PVOID AssociatedIrp. SystemBuffer

Покажчик на системний буфер для випадку, якщо пристрій підтримує буферизованная введення / висновок

PMDL MdlAddress

Покажчик на MDL список у випадку, якщо пристрій підтримує пряме введення / висновок

PVOID UserBuffer

Адреса для користувача буфера для введення / виводу

BOOLEAN Cancel

Індикатор того, що IRP пакет повинен бути анульований

Основне призначення осередків стека введення / виведення полягає в тому, щоб зберігати функціональний код і параметри запиту на ввід / вивід. Нижче, в таблиці 1.5.2 наводяться поля комірок стека вводу / виводу, до яких драйвер може звертатися безпосередньо за вказівником (чого не рекомендується робити для інших полів):

Таблиця 1.5.2. Структура комірки стека вводу / виводу

Поля

Опис

UCHAR MajorFunction

Код IRP _ MJ _ XXX, що описує призначення операції

UCHAR MinorFunction

Субкод операції

PDEVICE_OBJECT DeviceObject

Покажчик на об'єкт пристрою, якому був адресований даний об'єкт IRP

PFILE_OBJECT FileObject

Файловий об'єкт для даного запиту, якщо він заданий

union Parameters (трактування визначається значенням MajorFunction)

struct Read

Параметри для IRP типу IRP _ MJ _ READ:

ULONG Length

ULONG Key

LARGE_INTEGER ByteOffset

struct Write

Параметри для IRP типу IRP _ MJ _ WRITE:

ULONG Length

ULONG Key

LARGE_INTEGER ByteOffset

struct DeviceControl

Параметри для IRP типу IRP _ MJ _ DEVICE _ CONTROL:

ULONG OutputBufferLength

ULONG InputBufferLength

ULONG IoControlCode

PVOID Type3InputBuffer

Наведемо графічне представлення структури IRP пакету:

Рис. 1.5.1 Структура IRP пакету

Спілкування з USB накопичувачами в ОС Windows NT 5 на рівні драйверів, як вже було сказано в розділі 1.3.1, відбувається за допомогою передачі URB пакетів. Покажчики на URB пакети містять осередки стека IRP пакету, доступ до цих вказівниками здійснюється наступним чином:

...

PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp);

PURB Urb = IrpSp-> Parameters. Others. Argument 1;

...

Наведемо часткове оголошення структури з довідкової документації Microsoft. Відзначимо лише поля, використання яких необхідно в рамках даної курсової роботи:

typedef struct _URB {

union {

struct _URB_HEADER UrbHeader;

struct _URB_SELECT_INTERFACE UrbSelectInterface;

struct _URB_SELECT_CONFIGURATION UrbSelectConfiguration;

struct _URB_BULK_OR_INTERRUPT_TRANSFER UrbBulkOrInterruptTransfer;

}

} URB, * PURB;

Поле UrbHeader зберігає інформацію про код URB пакету, за яким можна визначити, яка операція запитується.

Поля UrbSelectInterface і UrbSelectConfiguration служать для запиту по вибору інтерфейсу та конфігурації пристрою, які будуть використовуватися при роботі з пристроєм. Пакети цієї структури відправляються хостом до пристрою на початку його роботи, при конфігуруванні.

Поле UrbBulkOrInterruptTransfer несе найважливішу в рамках даної курсової роботи інформацію - покажчики на блоки введення / виводу USB пристрою. Наведемо опис структури _URB_BULK_OR_INTERRUPT_TRANSFER:

struct _URB_BULK_OR_INTERRUPT_TRANSFER {

struct _URB_HEADER Hdr;

USBD_PIPE_HANDLE PipeHandle;

ULONG TransferFlags;

ULONG TransferBufferLength;

PVOID TransferBuffer;

PMDL TransferBufferMDL;};

Поля цієї структури описані в наступній таблиці:

Таблиця 1.5.3 Поля структури URB_BULK_OR_INTERRUPT_TRANSFER

Поле

Опис

struct _URB_HEADER Hdr

Стандартний заголовок URB   пакета, що містить код запиту

USBD_PIPE_HANDLE PipeHandle

Дескриптор каналу, на який передаються дані

ULONG TransferFlags

Прапори, що визначають напрямок передачі даних і спосіб обробки помилок

ULONG TransferBufferLength

Довжина передаваного блоку даних в байтах

PVOID TransferBuffer

Покажчик на переданий буфер. Буфер знаходиться в нестранічной пам'яті

PMDL TransferBufferMDL

Покажчик на MDL   список, що несе передану інформацію. Буфер знаходиться в сторінкової пам'яті

Слід зазначити, що один з покажчиків TransferBuffer або TransferBufferMDL дорівнює NULL, тобто в межах одного пакету передається тільки одна порція даних.

Завдання протоколювання обміну інформацією зводиться до перехоплення і збереження буферів TransferBuffer і TransferBufferMDL.

1.6 Рівні запиту переривань

У кожний момент часу центральний процесор знаходиться на одному з рівнів IRQL (Interrupt Request Level - рівень запитів переривань). Рівні IRQL розташовуються в порядку убування від HIGHEST _ LEVEL до PASSIVE _ LEVEL. Кожному з переривань (переривання від зовнішніх пристроїв, системний годинник, і т.д.) відповідає свій рівень IRQL. Спеціальним діям операційної системи також призначені IRQL. Вони відзначені в нижній частині наведеної таблиці:

Таблиця 1.6.1. Рівні запитів переривань.

Рівень

Призначення

HIGHEST_LEVEL

Найвищий рівень. Всі переривання заблоковані

POWER_LEVEL

Переривання по відмові харчування

IPI_LEVEL

Межпроцессорной взаємодія

CLOCK2_LEVEL

Переривання по системному таймеру 2

З LOCK1_LEVEL

Переривання по системному таймером 1

PROFILE_LEVEL

Переривання по таймеру виміру продуктивності

рівні DRQL

Звичайні переривання пристроїв

DISPATCH_LEVEL

Диспетчеризація потоків і виконання відкладених процедур

APC_LEVEL

Виконання асинхронного виклику процедури

PASSIVE_LEVEL

Звичайне виконання коду потоку

Загальне правило обробки рівнів запитів переривань свідчить, що переривання з IRQL, меншим, ніж у виконуваного в даний момент коду, маскуються. Під час виконання коду потоку (користувальницького або системного) встановлюється найменший IRQL = 0 (PASSIVE _ LEVEL). Робота драйвера найчастіше виконується на рівні IRQL = 2 (DISPATCH _ LEVEL). Рівні, що лежать над ним, називаються DIRQL (Device IRQL) і виставляються для обробників переривань від зовнішніх пристроїв (ISR - interrupt service routine). Навіть під час виконання ISR драйвера може відбутися переривання з великим IRQL, наприклад, що належить іншому драйверу.

Чим вище поточний рівень IRQL виконуваного коду, тим менше функцій йому доступно. Так, наприклад, диспетчер потоків працює на рівні
DISPATCH _ LEVEL, і, отже, не буде викликатися, поки на процесорі з рівнем більшим чи рівним DISPATCH _ LEVEL виповнюється інший код. Таким чином, на рівнях DISPATCH _ LEVEL і вище відключається перемикання потоків. Функції очікування диспетчерських об'єктів (події, м'ютекси, семафори) з відмінною від нуля часом, звернення до файлів, підкачка відсутніх у фізичній пам'яті сторінок - все це також стає недоступним. Для коректного збереження запитів у файлі фільтр у таких випадках повинен застосовувати спеціальну методику.

1.7 Повідомлення про завершення запиту нижчестоящим драйвером

При відстеження обміну даними драйвер-фільтр може отримувати повідомлення про те, що деякий переданий запит був завершений нижчестоящим драйвером. Механізм повідомлення полягає в тому, що викликом спеціальної функції IoSetCompletionRoutine фільтр звертається до стека в пакеті IRP. У позиції стека, наступної за поточною позицією, він встановлює в спеціальному полі адресу функції завершення (completion routine). Потім при передачі пакета по ланцюжку позиція стека збільшується.

Коли нижчий драйвер відправляє пакет запиту на завершення (викликом IoCompleteRequest), підсистема вводу   / Виводу починає проглядати стек всередині цього пакета від кінця до початку. Якщо в якійсь позиції стека визначена функція завершення, управління передається їй. Відпрацювавши, функція повертає результат, який сигналізує про успіх, помилку або необхідності подальшої обробки запиту.

При перших двох варіантах підсистема вводу / виводу переходить до наступної позиції стека і продовжує перегляд, поки не буде досягнуто його початок. Після цього запит завершується нормальним чином.

При третьому ж варіанті перегляд стека негайно припиняється і запит не буде завершено. Ця можливість реалізована для того, щоб драйвер-фільтр міг виконати будь-які дії над пакетом запиту після того, як той буде оброблений у нижчестоящих драйвері. Після такої «додаткової обробки» пакет знову повинен бути відправлений на завершення.

1.8 Робота з файлами в режимі ядра

Оскільки протоколіруемая інформація повинна зберігатися у файлі на диску, слід розглянути основні функції рівня ядра, використовувані при роботі з файлами.

Для відкриття файлу з драйвера режиму ядра використовується універсальна функція ZwCreateFile. Універсальність цієї функції полягає в тому, що з її допомогою здійснюється і відкриття існуючих файлів, і створення нових.

Специфіка системної функції ZwCreateFile полягає в тому, що вона має протокольних параметрів навіть більше, ніж користувальницький виклик CreateFile. Істотна частина вхідної інформації про відкривається об'єкті надходить всередині структури OBJECT _ ATTRIBURTES, яку слід заздалегідь створити і заповнити відповідними конкретними даними. Для ведення облікової інформації відкритого об'єкта використовується структура даних IO _ STATUS _ BLOCK, яку слід надати при виклику (ініціалізувати її не буде).

Наведемо основні параметри функції ZwCreateFile в наступній таблиці:

Таблиця 1.8.1. Параметри функції ZwCreateFile

Тип параметра

Опис параметра

OUT PHANDLE pHandle

Покажчик на змінну, куди слід помістити дескриптор відкритого об'єкта

IN ACCESS_MASK DesiredAccess

Характеристика доступу до об'єкта. Для фалів найчастіше використовуються значення

GENERIC_READ або GENERIC_WRITE

IN POBJECT_ATTRIBUTES

pObjAttributes

Покажчик на заповнену викликає кодом структуру даних, яка описує ім'я, місце розташування і деякі інші характеристики відкривається об'єкта

OUT PIO_STATUS_BLOCK pIOStatus

Покажчик на буфер, в якому буде розміщена інформація про відкритий об'єкті у форматі структури IO _ STATUS _ BLOCK

IN PLARGE_INTEGER AllocationSize

Початковий розмір файлу в байтах. Ненульове значення приймається до уваги тільки при створенні і перезапису файлу

IN ULONG FileAttributes

Атрибути файлу, що відкривається. Типовим є значення FILE _ ATTRIBUTE _ NORMAL

IN ULONG SharedAccessFlags

Описує, чи дозволений спільний доступ, наприклад, FILE _ SHARE _ READ - для читання

IN ULONG CreateDispositionFlags

Спосіб відкриття файлу, наприклад, FILE _ OPEN _ IF - якщо не існує, створити

IN ULONG CreateOptions

Комбінація прапорів створення, наприклад, FILE _ SYNCHONOUS _ IO _ NONALERT - всі операції над файлом виконуються як синхронні (DesiredAccess повинен включати прапор SYNCHRONIZE)

IN PVOID EaBuffer

Для драйверів пристроїв слід вказувати NULL

IN ULONG EaLength

Для драйверів пристроїв слід вказувати 0

Для заповнення структури атрибутів об'єкта використовується функція
InitializeObjectAttributes. Опишемо її параметри в наступній таблиці:

Таблиця 1.8.2. Параметри функції InitializeObjectAttributes

Тип параметра

Опис параметра

OUT POBJECT_ATTRIBUTES pObjAttributes

Покажчик на змінну, куди слід помістити атрибути об'єкта

IN PUNICODE_STRING ObjectName

Назва об'єкту, HANDLE якого створюється

IN ULONG Attributes

Прапори атрибутів об'єкта, при відкритті файлу як правило використовуються прапори OBJ _ CASE _ INSENSITIVE і OBJ _ KERNEL _ HANDLE

IN HANDLE RootDirectory

Дескриптор кореневої директорії для об'єкта, описувач атрибутів якого створюється. Якщо ObjectName повністю описує шлях до об'єкта, то значенню RootDirectory присвоюється NULL

IN PSECURITY_DESCRIPTOR SecurityDescriptor

Дескриптор безпеки. Якщо вказано NULL, то застосовується стандартний дескриптор

Запис у файл виконується системної функцією ZwWriteFile:

Таблиця 1.8.3. Параметри функції ZwWriteFile

Тип параметра

Опис параметра

IN HANDLE FileHandle

Дескриптор відкритого або модифікованого файлового об'єкта

IN HANDLE Event

Для драйверів пристроїв слід вказувати NULL

IN PIO_APC_ROUTINE

Для драйверів пристроїв слід вказувати NULL

IN PVOID ApcContext

Для драйверів пристроїв слід вказувати NULL

OUT PIO_STATUS_BLOCK pioStatusBlock

У полі pIoStatusBlock -> Information після завершення знаходиться число реально записаних байтів

IN PVOID Buffer

Буфер з даними для запису

IN ULONG Length

Розмір записуваної порції даних

IN PLARGE_INTEGER pByteOffset

Покажчик на змінну де міститься зсув у файлі від його початку, за яким слід робити запис

IN PULONG Key

Для драйверів пристроїв слід вказувати NULL

Для закриття дескриптора об'єкта слід застосовувати функцію ZwCloseKey.

Слід зазначити, що функції роботи з файлами можуть працювати тільки на рівні IRQL, рівному PASSIVE _ LEVEL. Це призводить до необхідності застосування спеціальної методики при протоколюванні обміну даними з USB накопичувачем.

1.9 Робота з реєстром в режимі ядра

Робота з реєстром з драйвера рівня ядра необхідна, так як саме в системному реєстрі зберігається інформація про налаштування протоколювання. Інформація про налаштування зберігається в ключі реєстру, пов'язаному з пристроєм, до якого підключається драйвер-фільтр. Ім'я цього пристрою відповідає шаблоном HKEY _ LOCAL _ MACHINE \ System \ CurrentControlSet \ Enum \ USB \ XXX \ XXX \ DeviceParameters.

Доступ ключу пристрою в реєстрі в драйвері надається функцією IoOpenDeviceRegistryKey. Перерахуємо її параметри:

Таблиця 1.9.1. Параметри функції IoOpenDeviceRegistry

Тип параметра

Опис параметра

IN PDEVICE_OBJECT DeviceObject

Покажчик на об'єкт фізичного пристрою, ключ якого повинен бути відкритий

IN ULONG DevInstKeyType

Параметр визначає, чи пов'язаний відкривається ключ безпосередньо з фіз. пристроєм або його програмним забезпеченням

IN ACCESS_MASK DesiredAccess

Цей параметр визначає права доступу до ключа

OUT PHANDLE DevInstRegKey

Покажчик на змінну, куди слід помістити дескриптор відкритого ключа

Відкривши основний ключ, слід отримати доступ до вкладеного в нього ключу з параметрами протоколювання. Для цього використовується функція ZwOpenKey. Перерахуємо її параметри:

Таблиця 1.9.2. Параметри функції ZwOpenKey

Тип параметра

Опис параметра

OUT PHANDLE KeyHandle

Покажчик на змінну, куди слід помістити дескриптор відкритого ключа

IN ACCESS_MASK DesiredAccess

Цей параметр визначає права доступу до ключа

IN POBJECT_ATTRIBUTES pObjectAttributes

Покажчик на заповнену викликає кодом структуру даних, яка при використанні у цій функції повинна містити ім'я відкривається ключа

Відкривши ключ власних параметрів драйверу необхідно вважати налаштування протоколювання. Для читання значення параметрів ключа реєстру використовується функція ZwQueryValueKey. Перерахуємо її параметри:

Таблиця 1.9.3. Параметри функції ZwQueryValueKey

Тип параметра

Опис параметра

IN HANDLE KeyHandle

Дескриптор ключа, якому належить зчитування параметр

IN PUNICODE_STRING ValueName

Рядок юнікод-символів, що містить ім'я параметра ключа

IN KEY_VALUE_INFORMATION_CLASS

KeyValueInformationClass

Цей параметр приймає одне з трьох значень в залежності від повноти інформації про параметр:

KeyValueBasicInformation

KeyValueFullInformation

KeyValuePartialInformation

OUT PVOID KeyInformation

Покажчик на буфер, виділений викликає кодом, до якого має бути поміщена запитувана інформація

IN ULONG Length

Довжина наданого буфера

OUT PULONG ResultLength

Покажчик на змінну, що містить число реально записаних в KeyInformation байт

Після того, як робота з ключем реєстру закінчена, його дескриптор слід звільнити викликом функції ZwClose.

1.10 MDL списки

MDL список - це структура, що зберігає відображення блоку віртуальної пам'яті на фізичну пам'ять. MDL список використовується в розробляється драйвері для зберігання інформації з URB пакетів, пов'язаних з введенням / виводом USB пристрою. Крім того, обмін інформацією з USB пристроєм в режимі прямого доступу до пам'яті ведеться саме за допомогою MDL списків.

Перед використанні MDL списку в драйвері необхідно провести ряд підготовчих дій:

  • виділити область у сторінкової пам'яті за допомогою виклику функції
    ExAllocatePool;

  • викликати функцію MmCreateMdl, створює і ініціюючих MDL список;

  • виконати фіксацію сторінок, описаних в MDL списку, у фізичній пам'яті за допомогою виклику функції MmProbeAndLockPages.

Після завершення використання MDL списку його слід звільнити:

  • скасувати фіксацію сторінок сторінкової пам'яті в оперативній пам'яті викликом функції MmUnlockPages;

  • очистити MDL список, викликавши функцію IoFreeMdl;

  • звільнити виділену під список сторінкову пам'ять викликом
    ExFreePool.

2. Конструкторський розділ

2.1 Точки входу розроблюваного драйвера

Розроблюваний драйвер є драйвером нижнього рівня. У стеці драйверів USB накопичувача він знаходиться безпосередньо під драйвером пристрою, якщо після його завантаження не відбудеться встановлення будь-якого іншого драйвера-фільтра нижнього рівня.

Розроблюваний драйвер включає в себе такі точки входу:

  • DriverEntry;

  • AddDevice;

  • DriverUnload;

  • Функції IRP пакетів:

  • обробка IRP   пакетів з кодами IRP_MJ_INTERNAL_DEVICE_CONTROL - функція DispatchInternalDeviceControl;

  • обробка IRP пакетів з іншими кодами - функція DispatchRoutine.

Розглянемо кожну з них більш докладно.

2.1.1 Функція DriverEntry

У цій функції відбувається реєстрація всіх стандартних точок входу драйвера і обробників IRP пакетів. У розробляється драйвері пакети IRP c кодами, не рівними IRP _ MJ _ INTERNAL _ DEVICE _ CONTROL обробляються функцією DispatchRoutine.

2.1.2 Функція AddDevice

Управління цієї функції передається диспетчером введення / виведення після того, як завершує свою роботу DriverEntry. AddDevice створює функціональний об'єкт пристрою за допомогою виклику IoCreateDevice і підключає його до стека драйверів вибраного пристрою (викликом IoAttachDeviceToDeviceStack). Крім того, у цій функції здійснюються дії з підготовки до протоколювання: зчитуються налаштування з системного реєстру, виділяється буфер для збору протоколіруемой інформації, створюється лог-файл.

2.1.3 Функція DriverUnload

Функція DriverUnload необхідна для того, щоб зробити драйвер вивантажуємо. У успадкованих драйверах на цю функцію покладено весь процес вивантаження драйвера: видалення символьних посилань, об'єктів пристроїв драйвера, відключення переривань від об'єктів, звільнення виділеної пам'яті. У WDM драйвери всі ці дії покладені на функцію-обробник пакетів з кодом IRP _ MJ _ PNP.

2.1.4 Функція DispatchRoutine

На цю функцію покладено обов'язки з обробки IRP пакетів з різними кодами, хоча в розробляється драйвері існує необхідність в обробці тільки двох типів запитів. Всі запити з кодом, відмінним від IRP _ MJ _ PNP передаються по стеку драйверів без змін. Запити ж IRP _ MJ _ PNP діспетчерізуются за суб-кодами функції PnP _ Dispatch. Необхідність диспетчеризації за суб-кодами запитів IRP _ MJ _ PNP викликана тим, що драйвер не повинен порушувати порядку роботи операційної системи і зобов'язаний підкорятися PnP менеджеру, тобто в драйвері повинні коректно оброблятися події старту і видалення пристрою.

2.1.5 Функція DispatchInternalDeviceControl

Запити вводу / виводу до USB   накопичувачу передаються у складі IRP   пакетів з кодом IRP _ MN _ INTERNAL _ DEVICE _ CONTROL. Цей пакет містить повну інформацію про направлення і характер переданих даних. Тобто для протоколювання обміну інформацією з USB носієм слід перехоплювати пакети саме цього типу.

Для того щоб перехоплювати інформацію, передану в обох напрямках, слід встановити функцію зворотного виклику диспетчера введення / виводу. Методика установки цієї функції була описана в розділі 1.7. За наявності цієї функції розробляється драйвер-фільтр отримає можливість перехоплення даних, переданих від пристрою до хосту.

Для збереження протоколіруемой інформації використовується, як вже було сказано в розділі 1.10, MDL список. Цей MDL список створюється у функції AddDevice. Обсяг пам'яті, виділеної під список, збігається з максимальним розміром лог-файлу, що задається в призначеному для користувача додатку. Після створення список фіксується в сторінкової пам'яті, що запобігає його вивантаження на жорсткий диск під час роботи драйвера. Після цих підготовчих дій список використовується у функції DispatchInternalDeviceControl - Він заповнюється перехоплюваних інформацією.

Запис накопиченого буфера в лог-файл відбувається при видаленні пристрою.

Така методика обрана через те, що функція DispatchInternalDeviceControl працює на рівні запиту переривань, рівному DISPATCH _ LEVEL, що сильно ускладнює використання механізмів синхронізації, які могли б дозволити перейти на рівень запиту переривань, рівний PASSIVE _ LEVEL, де стають доступними функції роботи з файлами. Якщо б це було досягнуто в розробляється драйвері, то відпала б необхідність виділення великих об'ємів нестранічной пам'яті для зберігання протоколу.

Запис файлу на диск у момент видалення пристрою може, так як ця подія ініціалізується PnP менеджером, запити якого завжди відбуваються на рівні IRQL, рівному PASSIVE _ LEVEL.

2.2 Розміщення коду драйвера в пам'яті

Деякі функції драйвера, наприклад ті, які виконують ініціалізацію, вигідно виконати і звільнити пам'ять, зайняту ними. У мові C є спеціальна директива # pragma _ alloc _ text (<Тип секції>, <ім'я розміщується функції>;), що дозволяє управляти розміщенням коду. В якості типу секції можуть зазначатися значення «INIT» або «PAGE».

Функції з розміщенням в секції «INIT» вивантажуються, і пам'ять, зайнята ними, звільняється відразу по завершенні їх роботи. У розробляється драйвері в секції «INIT» розміщена точка входу DriverEntry, оскільки вона виконується один раз при завантаженні драйвера.

Точки входу AddDevice і DriverUnload розташовуються в секції «PAGE», тобто в сторінкової пам'яті, оскільки вони гарантовано викликаються на рівні привілеїв, рівному PASSIVE _ LEVEL і, навіть опинившись вивантаженими на диск, будуть негайно завантажені у фізичну пам'ять менеджером сторінкової пам'яті (який здатний працювати тільки на рівні PASSIVE _ LEVEL).

Решта ж точки входу (DispatchRoutine і DispatchInternalDeviceControl) розташовуються за замовчуванням, в нестранічной пам'яті, оскільки їх робота залежить від клієнтського драйвера USB пристрою, під яким у стеку драйверів розташовується розробляється драйвер-фільтр. Рівень привілеїв його запитів слабо передбачуваний і може бути рівний DISPATCH _ LEVEL. На цьому рівні підкачка сторінок неможлива, що при зверненні до вивантажений функції призведе до краху системи.

2.3 Установка драйвера в системі

Для установки драйвера слід створити його ключ в системному реєстрі.
Ім'я ключа має мати наступний вигляд:

HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Services \ driver_name.

Остання частина імені специфічна для встановлюваного драйвера. Створюваний ключ повинен містити наступні параметри:

  • DisplayName - значення цього параметра описує текст, використовуваний у службових програмах;

  • ErrorControl - цей параметр наказує операційній системі спосіб поведінки в тій ситуації, коли при завантаженні драйвера сталася помилка;

  • ImagePath - описує повний шлях до файлу з виконуваним кодом драйвера;

  • Start - описує стадію завантаження операційної системи, коли слід завантажувати драйвер;

  • Type - визначає тип драйвера.

Можливі значення вказаних параметрів можна дізнатися з документації MSDN.

Таким чином, перша стадія установки драйвера в систему полягає
в тому, що повинен бути створений ключ драйвера в реєстрі, а сам драйвер
скопійований в каталог, описуваний рядком ImagePath (як правило -% SystemRoot% \ System 32 \ Drivers \ driver _ name. sys).

Далі повинно бути вибрано пристрій, на який буде встановлений фільтр. Вибраного пристрою в системному реєстрі відповідає ключ з ім'ям виду HKLM \ CurrentControlSet \ Enum \ USB \ XXX \ YYY.

Остання частина ключа (XXX \ YYY) визначається ім'ям пристрою. При установці драйвера фільтра нижнього рівня в розділі YYY створюється рядковий параметр LowerFilters, якому присвоюється значення, що збігається з ім'ям драйвера, для якого був створений ключ у ... \ Services. Таким чином, при підключенні пристрою до системи для нього буде створено стек драйверів, до складу якого в якості фільтра нижнього рівня буде завантажений встановлюваний драйвер.

Крім того, в ключі, пов'язаному з пристроєм, при установці драйвера створюється додатковий розділ MyFilterParams, який зберігає два параметри:

  • MaxLogSize - максимальний розмір лог-файлу;

  • LogFileName - ім'я лог-файлу.

3. Технологічний розділ

3.1 Вибір мови і засобів програмування

Розроблюваний програмний комплекс складається з двох частин:

  • Драйвера-фільтра;

  • Керуючого додатки для встановлення фільтру і введення параметрів протоколювання.

Кожна з програм, здійснює спілкування з операційною системою на різному рівні. Відповідно необхідний різний підхід до цих завдань і спеціальний підбір засобів розробки.

3.1.1 Драйвер-фільтр

Від розроблюваного драйвера-фільтра потрібно висока швидкість роботи та надійність. При його роботі здійснюється безліч маніпуляцій з пам'яттю, операцій з покажчиками, перетворень типів. Важливо уявляти структуру скомпільованого продукту, щоб правильно уявити собі логіку його роботи. Серед мов програмування, що задовольняють цим вимогам, відомі мови С і асемблер. Для написання драйвера перевагу було віддано мови C. Такий вибір був зроблений з наступних причин:

  • Існує спеціальний компілятор C, що поставляється в складі пакету DDK, призначений спеціально для компіляції драйверів. Він містить безліч макроозначень і бібліотек, що дозволяють зробити процес написання драйвера більш легким. Microsoft рекомендує його як основну середовище для розробки драйверів для Windows;

  • Програми на асемблері працюють швидше, ніж програми, написані на C. Але різниця в швидкості між цими мовами не дуже велика. Зате продуктивність праці при використанні C набагато вище, ніж при використанні асемблера.

3.1.2 Управляє додаток

Управляє додаток було створено в середовищі розробки Borland C + + Builder, оскільки це середовище програмування надає широкі можливості по створенню користувальницького інтерфейсу і прискорює процес розробки програмних продуктів. Дане середовище програмування містить безліч стандартних елементів віконного користувальницького інтерфейсу, використання яких дозволило зробити управляє додаток простим і зрозумілим для користувача. Для розглянутого програми швидкість роботи і обсяг виконуваного файлу не є критичними факторами, тому вибір середовища Borland C + + Builder можна вважати цілком обгрунтованим.

3.2 Структури даних драйвера-фільтра

Для збору інформації про введення / виведення пристрою використовується структура BUFFER, оголошена в драйвері наступним чином:

typedef struct _BUFFER

{

PVOID Buffer;

PMDL Mdl;

ULONG MaxSize;

ULONG CurrentSize;

} BUFFER, * PBUFFER;

Пояснимо значення полів структури:

  • Buffer - покажчик на буфер, який зберігає інформацію;

  • Md l - покажчик на MDL список, який зберігає відображення буфера на системні сторінки в пам'яті;

  • MaxSize - максимальний розмір буфера в байтах;

  • CurrentSize - поточний розмір буфера в байтах.

У програмуванні вважається поганим тоном створення змінних, глобальних для всього коду. У драйверах рекомендується розміщувати ці глобальні змінні в структурі розширення пристрою. Створення цієї структури, кінцевий вигляд якої визначається програмістом, відбувається при виклику IoCreateDevice, тобто при створенні об'єкта функціонального пристрою драйвера. Покажчик на розширення пристрою може бути отриманий за вказівником на об'єкт пристрою.

У розробляється драйвері у розширенні пристрою розташована наступна інформація:

typedef struct _DEVICE_EXTENSION

{BUFFER UrbPackets;

HANDLE LogFileHandle;

BOOLEAN PreparedToLog;

PURB Urb;

ULONG UrbCount;

PDEVICE_OBJECT topDevObject;

...

} DEVICE _ EXTENSION, * PDEVICE _ EXTENSION;

Пояснимо призначення зазначених змінних:

  • UrbPackets - Буфер для протоколювання;

  • LogFileHandle - дескриптор файлу, в який записується інформація про введення / виведення;

  • PreparedToLog - прапор, який вказує на те, що підготовка до протоколювання пройшла успішно;

  • Urb - пакет, інформація про який переноситься в буфер UrbPackets;

  • UrbCount - число URB пакетів, що пройшли через фільтр;

  • topDevObject - об'єкт пристрою, що знаходиться в стеку під нашим пристроєм.

3.2 Інтерфейс керуючого програми

Управляє додаток призначений для установки драйвера-фільтра в системі та передачі йому параметрів протоколювання через системний реєстр.

Інтерфейс керуючого програми складається з головного вікна, представленого на малюнку 3.2.1:

Рис. 3.2.2 Інтерфейс керуючого програми

У лівій частині вікна розташований список, в якому відображаються імена пристроїв, присутніх у системі.

Перед установкою драйвера-фільтра на деякий USB накопичувач, слід створити ключ драйвера в системному реєстрі. Для цього слід розмістити файл драйвера MyUSBFlt. Sys в кореневому каталозі керуючого програми і натиснути кнопку «Створити ключ драйвера». Після закінчення операції буде видане повідомлення про її результаті.

Установка драйвера-фільтра допускається тільки на пристрої типу «Запам'ятовуючі пристрої USB». Для цього пристрій слід виділити в списку, а потім ввести максимальний розмір лог-файлу і шлях до нього. Після цього слід натиснути кнопку «Встановити драйвер». У залежності від результату установки буде видане повідомлення про успіх операції або її невдачі.

Драйвер-фільтр почне свою роботу після того, як пристрій, на який він був установлений, буде знову. Перезапуск можна здійснити шляхом виділення потрібного пристрою в списку і натискання кнопки «Перезапустити пристрій». Того ж результату можна домогтися, перезапустивши пристрій типу «Хост-контролер» або «Кореневий концентратор», до якого підключений розглянутий USB накопичувач. Можливий також перезапуск пристрою шляхом від'єднання від порту концентратора і подальшого підключення до нього.

Видалення драйвера-фільтра з системи має здійснюватися у зворотному порядку: спочатку видаляється драйвер з пристрою, а потім ключ драйвера. Це пов'язано з тим, що у випадку, якщо буде вилучений ключ драйвера, а сам пристрій не буде звільнений від фільтра, то робота пристрою буде блокована. Але після повного видалення фільтра з системи і перезапуску пристрою, його робота буде відбуватися в звичайному режимі.

3.3 Тестування драйвера-фільтра

Драйвер був протестований з використанням стандартної тестуючої утиліти DriverVerifier, що поставляється у складі пакету DDK. За допомогою цієї утиліти були проведені наступні тести:

  • Операції з пулами пам'яті;

  • Коректність рівнів IRQL, на яких виконується код драйвера;

  • Брак ресурсів;

  • Нетипові запити до драйвера.

Всі тести пройшли успішно. Пам'ять в системі розподілялася правильно, помилок з нею не виникало. На брак ресурсів драйвер реагував коректно. Нетипові запити до драйвера не оброблялися ім.

Для налагодження драйвера використовувалася програма DebugView. Ця утиліта дозволяє здійснювати перехоплення повідомлень налаштування, які видаються драйвером.

Висновок

У даній роботі розглянуто питання, пов'язане з розробкою драйверів пристроїв у системі Windows, і реалізований драйвер-фільтр USB накопичувача.

Розроблений драйвер надає наступні можливості:

  • можливість установки на будь-який USB накопичувач, присутній в системі;

  • перехоплення інформації вводу / виводу USB накопичувача;

  • запис перехопленої інформації у файл на диску.

Драйвер-фільтр був протестований з допомогою тестових утиліт зі складу пакету DDK і відповідає всім сучасним вимогам, що накладаються ОС Windows на характеристики драйверів.

Тим не менше, існують шляхи щодо удосконалення розробленого програмного комплексу. Наприклад, існує можливість по створенню зв'язки «драйвер рівня ядра - котра управляє додаток користувальницького рівня », яка за наявності розвинених механізмів синхронізації дозволила б отримувати інформацію про введення / виведення пристрою в інтерактивному режимі. Але це досить трудомістке завдання, що виходить за рамки курсової роботи.

Список літератури та інтернет-ресурсів

1. Агуров П.В. Інтерфейси USB. Практика використання й програмування. - СПб.: БХВ-Петербург, 2004. - 576 с.

2. Солдатов В.П. Програмування драйверів Windows. Вид. 2 - е, перероб. і доп. - М.: ТОВ «Біном-Пресс», 2004. - 480 с.

3. Матеріали проекту «Windows Assembly Site» - www.wasm.ru.

4. Матеріали сайту www. Usb. Org.

5. MSDN Library, Copyright 1987-2005 Microsoft Corporation.

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Курсова
230.3кб. | скачати


Схожі роботи:
Протокол обміну керуючими повідомленнями ICMP Протоколи обміну маршрутною інформацією
Створення ігрової програми шашки для гри між людиною і комп`ютером на мові С Builder
Протоколи обміну маршрутною інформацією стека TCPIP
Використання TCPIP протоколу для обміну інформацією в мережі
Основи роботи з комп`ютером
Як вижити працюючи з комп`ютером
Робота з персональним комп`ютером
Правила роботи учнів з комп`ютером
Гра в Морський бій з комп`ютером
© Усі права захищені
написати до нас